﻿using System;
using Ewk.Math.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ewk.Math.ComputationTypes.UnitTests
{
    [TestClass]
    public class ComputableFactoryProviderUnitTests
    {
        [TestMethod]
        public void ComputableFactoryProvider_provides_a_ComputableFactory_for_the_specified_type()
        {
            var computableFactory = Computable<double>.Factory;

            Assert.IsInstanceOfType(computableFactory, typeof(IComputableFactory<double>));
        }

        [TestMethod]
        public void ComputableFactoryProvider_provides_a_ComputableFactory_that_can_produce_a_Computable()
        {
            var computableFactory = Computable<double>.Factory;
            var computable = computableFactory.Zero;

            Assert.IsInstanceOfType(computable, typeof(Computable<double>));
        }

        [TestMethod]
        public void ComputableFactoryProvider_returns_true_when_a_type_is_registered_that_not_has_already_been_registered()
        {
            var result = ComputableFactoryProvider.RegisterFactory(new ComputableInt16TestFactory());
            Assert.IsTrue(result);
        }

        [TestMethod]
        public void ComputableFactoryProvider_returns_false_when_a_type_is_registered_that_has_already_been_registered()
        {
            var result = ComputableFactoryProvider.RegisterFactory(new ComputableInt32TestFactory());
            Assert.IsFalse(result);
        }

        [TestMethod, ExpectedException(typeof(InvalidOperationException))]
        public void ComputableFactoryProvider_throws_an_InvalidOperationException_when_a_type_is_requested_that_has_not_been_registered()
        {
            var factory = Computable<long>.Factory;
        }

        [TestMethod]
        public void ComputableFactoryProvider_can_provide_a_ComputableFactory_that_can_produce_a_ComputableFunction_double_Complex()
        {
            var computableFactory = Computable<Function<double, Complex>>.Factory;
            var zero = computableFactory.Zero;

            Assert.IsInstanceOfType(zero, typeof(Computable<Function<double, Complex>>));

            var zeroResult = zero.Value.Func(Computable<double>.Factory.One);
            Assert.IsInstanceOfType(zeroResult, typeof(Computable<Complex>));
            Assert.AreEqual(zeroResult, Computable<Complex>.Factory.Zero);
        }

        class ComputableInt16Test : Computable<short>
        {
            #region Overrides of ComputableBase<short>
            public override Computable<short> Add(Computable<short> other)
            {
                throw new NotImplementedException();
            }

            public override Computable<short> Subtract(Computable<short> other)
            {
                throw new NotImplementedException();
            }

            public override Computable<short> Multiply(Computable<short> other)
            {
                throw new NotImplementedException();
            }

            public override Computable<short> Divide(Computable<short> other)
            {
                throw new NotImplementedException();
            }

            public override Computable<short> Sqrt()
            {
                throw new NotImplementedException();
            }

            public override Computable<short> Pow(Computable<short> power)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        class ComputableInt16TestFactory : IComputableFactory<short>
        {
            #region Implementation of IComputableFactory<short>

            public Computable<short> Zero
            {
                get { return new ComputableInt16Test(); }
            }

            public Computable<short> Epsilon
            {
                get { return new ComputableInt16Test(); }
            }

            public Computable<short> One
            {
                get { return new ComputableInt16Test(); }
            }

            public Computable<short> MinValue
            {
                get { return new ComputableInt16Test(); }
            }

            public Computable<short> MaxValue
            {
                get { return new ComputableInt16Test(); }
            }

            public Computable<short> Create(short value)
            {
                return new ComputableInt16Test();
            }

            #endregion
        }

        class ComputableInt32TestFactory : IComputableFactory<int>
        {
            #region Implementation of IComputableFactory<int>

            public Computable<int> Zero
            {
                get { throw new NotImplementedException(); }
            }

            public Computable<int> Epsilon
            {
                get { throw new NotImplementedException(); }
            }

            public Computable<int> One
            {
                get { throw new NotImplementedException(); }
            }

            public Computable<int> MinValue
            {
                get { throw new NotImplementedException(); }
            }

            public Computable<int> MaxValue
            {
                get { throw new NotImplementedException(); }
            }

            public Computable<int> Create(int value)
            {
                throw new NotImplementedException();
            }

            #endregion
        }
    }
}
